/**
 * @file   MiraVector2D.h
 * @author Josep Sabater (jsabaterm@el.ub.edu)
 * @date   September, 2015
 * @version 1.0.0
 * @ingroup MiraGeometryLib
 * @brief  Interface of a vector of 2 dimensions.
 *
 *
 */
#ifndef MIRA_VECTOR_2D_H
#define MIRA_VECTOR_2D_H

#include <vector>
#include <memory>
#include <iosfwd> // Add only forward declarations for ostream

#include "MiraOrientation.h"
#include "MiraAffineTrans2D.h"

namespace MiraGeometryLib{

   /**
    * @class Vector2D
    * @brief Implementation of the Vector 2D IFace
    */
    class Vector2D {

    public:
        
        //! Container <Vector> of Vector2D
        typedef std::vector<MiraGeometryLib::Vector2D> Vector2DCont_t;
        
        // ! A Matrix of Vector2D
        typedef std::vector<Vector2DCont_t> Vector2DMatrix_t;
                
        //! Unique pointer (C++11) to a Container of Vector2D
        typedef std::unique_ptr<Vector2DCont_t> Vector2DContUnique_t;
        
       /**
        * @brief Constructor for Vector2D.
        * @details This is the default constructor.
        * @return A vector2D object where its components (x, y) are both 0.
        */
       Vector2D(): x_(0), y_(0){};

       /**
        * @brief Constructor for Vector2D.
        * @details Constructor that specifies the values of the vector.
        * @param x Component X.
        * @param y Component Y.
        * @return A vector2D object where its components (x, y) are the ones
        * given by the caller.
        */
       Vector2D(const double x, const double y) : x_(x), y_(y){};
       
       /**
        * @brief Constructor for Vector2D.
        * @details Constructor that specifies the values of the vector.
        * @param c ordCont vector containing the two coords of the vector.
        * @return A vector2D object where its components (x, y) are the ones
        * given by the caller.
        */
       Vector2D(const std::vector<double> &coordCont);       
        
       /**
        * @brief Destructor
        */        
       ~Vector2D();       
       
       /**
        * @brief Copy Constructor for Vector2D
        */       
       Vector2D(const Vector2D& v) : x_(v.x_), y_(v.y_) {};      
       
       inline double x() const { return x_; };
       inline void x(const double& x) { x_ = x; };
       inline double y() const { return y_; }
       inline void y(const double& y) { y_ = y;};

       Vector2D& operator = (Vector2D vect);
       bool operator == (const Vector2D& vect) const;
       bool operator != (const Vector2D& vect) const;

       Vector2D& operator += (const Vector2D& vect);
       Vector2D& operator -= (const Vector2D& vect);
       Vector2D& operator *= (const Vector2D& vect);
       Vector2D& operator /= (const Vector2D& vect);
       Vector2D operator + (const Vector2D& vect) const;
       Vector2D operator - (const Vector2D& vect) const;
       Vector2D operator * (const Vector2D& vect) const;
       Vector2D operator / (const Vector2D& vect) const;

       Vector2D& operator *= (const double& scalar);
       Vector2D& operator /= (const double& scalar);
       Vector2D operator * (const double& scalar) const;
       Vector2D operator / (const double& scalar) const;

       friend Vector2D operator * (const double& scalar,
                                   const Vector2D& vect);
       Vector2D operator - () const;
       
       inline double operator[](int idx) { return idx == 0 ? x_ : y_; }
    
       friend std::ostream& operator << (std::ostream& os, const Vector2D& vect);
       double dot (const Vector2D& vect) const;
       double cross (const Vector2D& vect) const;
       double squaredNorm () const;
       double norm() const;
       /**
        * @brief Angle in radians between this vector and î and ĵ frame.
        * @return A real value expressing the angle in radians.
        * @retval num A positive or negative real value
        *                      <ul>
        *                         <li> num > 0: if vector is in 1st and 2nd quadrant.
        *                         <li> num < 0: if vector is in 3rd and 4th quadrant.
        *                      </ul>
        */
       double angle() const;
       /**
        * @brief Angle without sign in radians between this and the given one.
        * @return A real positive value expressing the angle in radians.
        */
       double angle(const Vector2D &vect) const;
       /**
        * @brief Angle with sign in radians between this vector and the given one.
        * @return A real value expressing the angle in radians.
        * @retval num A positive or negative real value.
        */
        double angleWithSign(const Vector2D &vect) const;
        Vector2D unit() const;
        bool isZero() const;

        /**
         * @brief Determines if the Vector2D is in the left or right region
         * of a 2D Segment.
         * @param va Vector2D indicating one end of the segment.
         * @param vb Vector2D indicating the other end of the segment.
         * @return A real number (num)
         * @retval  num the position of the vector respect to the segment
         *                      <ul>
         *                         <li> num > 0: Vector lies in the left area.
         *                         <li> num < 0 : Vector lies in the right area.
         *                      </ul>
         */
        double positionLine(const Vector2D& va, const Vector2D& vb);

        /**
         * @brief Rotates a Vector2D along axis Z.
         * @param rad A real value indicating an angle in radians.
         * @return A Vector 2D.
         */
        Vector2D rotate(const double& rad) const;
        Vector2D translate(const double& x, const double& y) const;
        Vector2D translate(const Vector2D& v) const;
        Vector2D perpendicular(const Orientation& o) const;
        Vector2D transform(const AffineTrans2D& aff) const;
        
        /**
         * @brief Vertical mirror (Mirrors the vector along axis X.)
         * @return A Vector 2D.
         */        
        inline Vector2D mirrorX() const { return Vector2D(x_, -(y_)); };
        
        /**
         * @brief Horizontal mirror (Mirrors the vector along axis Y.)
         * @return A Vector 2D.
         */                
        inline Vector2D mirrorY() const { return Vector2D(-(x_), y_); };

        /**
         * @brief Determines if two vector components are close enough.
         * @param vect A 2D Vector.
         * @return True if they are close. Otherwise, false.
         */
        bool close(const Vector2D& vect) const;

        /**
         * @brief Determines if two vector components are close enough.
         * @param vect A 2D Vector.
         * @decDigits double max error between both vectors
         * @return True if they are close. Otherwise, false.
         */
        bool close(const Vector2D& vect, const double& error) const;

   private:
       double x_; //! < Component X of the 2D vector.
       double y_; //! < Component Y of the 2D vector.

       static const int zeroRoundDigits_;
       static const double errorCheck_;

       /**
        * @brief Swaps of the date of given 2D vector with the data of this Vector2D.
        * @details This member function should never fail.
        * @param vect Vector2D to copy data from.
        * @return Nothing
        * @see        Assignment Operator in C++
        * @see        https://en.wikipedia.org/wiki/Assignment_operator_(C%2B%2B)
        */
       void swap_(Vector2D& vect);
    };
}

#endif
